//********************************************************************
//	created:	15:10:2011   22:00
//	filename: 	socketmanager.cpp
//	author:		tiamo
//	purpose:	socket manager
//********************************************************************

#include "stdafx.h"
#include "socketmanager.h"

namespace network
{
	//
	// add socket
	//
	bool SocketManager::addSocket(utils::SmartPointer<AsyncSocket> const& theSocket)
	{
		utils::Locker locker(mSocketListLock);
		if(mSocketList.size() >= mMaxOpenCount)
			return false;

		auto it																= mSocketList.find(theSocket->getHandle());
		if(it != mSocketList.end())
			ThrowException(L"SocketManager(%p): socket(%p) already in list", this, theSocket.get());

		if(!onAddSocket(theSocket))
			return false;

		theSocket->setSocketManager(this);
		mSocketList.insert(std::make_pair(theSocket->getHandle(), theSocket));
		return true;
	}

	//
	// remove socket
	//
	bool SocketManager::removeSocket(utils::SmartPointer<AsyncSocket> const& theSocket)
	{
		{
			utils::Locker locker(mSocketListLock);
			auto it															= mSocketList.find(theSocket->getHandle());
			if(it == mSocketList.end())
				return false;

			mSocketList.erase(it);
		}

		{
			utils::Locker locker(mFlushSocketListLock);
			auto it															= mFlushSocketList.find(theSocket->getHandle());
			if(it != mFlushSocketList.end())
				mFlushSocketList.erase(it);
		}

		onRemoveSocket(theSocket);
		return true;
	}

	//
	// close socket
	//
	void SocketManager::closeSocket(utils::SmartPointer<AsyncSocket> const& theSocket, bool errorState)
	{
		//
		// remove from socket manager, and close it
		//
		if(removeSocket(theSocket))
		{
			LogDebug(L"SocketManager(%p): close socket (%lu) connected to (%ls) with %lserror", this, theSocket->getHandle(), theSocket->getAddress(false).toString(true).c_str(), errorState ? L"" : L"no ");
			theSocket->close(errorState);
		}
	}

	//
	// close all socket
	//
	void SocketManager::closeAllSocket(bool noEvent)
	{
		{
			utils::Locker locker(mFlushSocketListLock);
			mFlushSocketList.clear();
		}

		utils::Locker locker(mSocketListLock);
		while(!mSocketList.empty())
		{
			auto it															= mSocketList.begin();
			utils::SmartPointer<AsyncSocket> theSocket						= it->second;
			mSocketList.erase(it);
			onRemoveSocket(theSocket);
			if(noEvent)
				theSocket->setSocketManager(nullptr);
			theSocket->close(true);
		}
	}

	//
	// network event
	//
	void SocketManager::onNetworkEvent(utils::SmartPointer<AsyncSocket> const& theSocket, bool readEvent, bool writeEvent, bool errorEvent)
	{
		theSocket->onNetworkEvent(readEvent, writeEvent, errorEvent);
	}

	//
	// push connected packet
	//
	void SocketManager::pushConnectedPacket(AsyncSocket* theSocket, bool errorState)
	{
		SocketEvent socketEvent(SocketEvent::Connected, theSocket, errorState);
		mSocketEventQueue.push(std::move(socketEvent));
	}

	//
	// push data packet
	//
	void SocketManager::pushDataPacket(AsyncSocket* theSocket, utils::SmartPointer<BasePacket>&& thePacket)
	{
		SocketEvent socketEvent(theSocket, std::move(thePacket));
		mSocketEventQueue.push(std::move(socketEvent));
	}

	//
	// push closed packet
	//
	void SocketManager::pushClosedPacket(AsyncSocket* theSocket, bool errorState)
	{
		SocketEvent socketEvent(SocketEvent::Closed, theSocket, errorState);
		mSocketEventQueue.push(std::move(socketEvent));
	}

	//
	// add flush socket
	//
	void SocketManager::addFlushSocket(AsyncSocket* theSocket)
	{
		utils::Locker locker(mFlushSocketListLock);
		mFlushSocketList.insert(std::make_pair(theSocket->getHandle(), theSocket));
	}

	//
	// process socket event
	//
	void SocketManager::processSocketEvent()
	{
		SocketEvent socketEvent;
		while(mSocketEventQueue.pop(socketEvent))
		{
			utils::SmartPointer<AsyncSocket> const& theSocket				= socketEvent.getSocket();
			try
			{
				switch(socketEvent.getEventType())
				{
				case SocketEvent::Connected:
					if(theSocket->getConnectCallback())
						theSocket->getConnectCallback()(theSocket.get(), socketEvent.getErrorState());
					break;

				case SocketEvent::Closed:
					if(theSocket->getCloseCallback())
						theSocket->getCloseCallback()(theSocket.get(), socketEvent.getErrorState());
					theSocket->clearCallback();
					break;

				case SocketEvent::DataPacket:
					if(theSocket->getDataPacketCallback())
						theSocket->getDataPacketCallback()(theSocket.get(), socketEvent.getPacket().get());
					break;
				}
			}
			catch(std::exception& e)
			{
				LogError(L"SocketManager(%p): process socket(%ld:%ls) event failed(%ls)", this, theSocket->getHandle(), theSocket->getAddress(false).toString(true).c_str(), utils::Exception::getMessage(e).c_str());
				closeSocket(theSocket, true);
			}
		}
	}

	//
	// flush socket buffer
	//
	void SocketManager::flushSocketBuffer()
	{
		decltype(mSocketList) flushList;

		{
			utils::Locker lock(mFlushSocketListLock);
			flushList														= std::move(mFlushSocketList);
		}

		for(auto it = flushList.begin(); it != flushList.end(); ++ it)
			it->second->flushWriteBuffer();
	}

	//
	// constructor
	//
	SocketEvent::SocketEvent(uint32_t eventType, utils::SmartPointer<AsyncSocket> const& theSocket, bool errorState)
		: mSocket(theSocket)
	{
		mEventType															= eventType;
		mErrorState															= errorState;
	}

	//
	// constructor
	//
	SocketEvent::SocketEvent(utils::SmartPointer<AsyncSocket> const& theSocket, utils::SmartPointer<BasePacket>&& thePacket)
		: mSocket(theSocket), mPacket(std::move(thePacket))
	{
		mErrorState															= false;
		mEventType															= DataPacket;
	}
}
